package manager

import "strings"

const (
	TypeImage           = "image"
	ActionReplace       = "replace"
	ActionEcho          = "echo"
	ActionFeatureBundle = "bundle"
)

type ImageFeature = string

const (
	ImageFeatureBundle ImageFeature = "bundle"
)

func init() {
	ActionInfoManager.RegisterActionInfo(&ActionInfo{
		Name:     ActionReplace,
		Type:     TypeImage,
		Discript: "替换镜像仓库",
	})
	ActionInfoManager.RegisterActionInfo(&ActionInfo{
		Name:     ActionEcho,
		Type:     TypeImage,
		Discript: "打印镜像信息",
	})
	ActionInfoManager.RegisterActionInfo(&ActionInfo{
		Name:     ActionFeatureBundle,
		Type:     TypeImage,
		Discript: "自动解析bundle镜像",
	})
}

type ImageAction interface {
	Action(image *MultiArchImage)
}

type ImageAct func(image *MultiArchImage)

func (a ImageAct) Action(image *MultiArchImage) {
	a(image)
}

type ImageReference string

func (n ImageReference) String() string {
	return string(n)
}

// Repo returns the repo of the image,
// e.g. "build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest"
//
//	build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle
func (n ImageReference) Repo() string {
	sp := strings.Split(string(n), ":")
	return strings.Join(sp[:len(sp)-1], ":")
}

// Domain returns the domain of the image,
// e.g. "build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest"
//
//	"build-harbor.alauda.cn"
func (n ImageReference) Domain() string {
	return strings.SplitN(string(n), "/", 2)[0]
}

// Path returns the path of the image,
// e.g. "build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest"
//
//	"middleware/mysql-mgr-operator-bundle:latest"
func (n ImageReference) Path() string {
	sp := strings.SplitN(string(n), "/", 2)
	return sp[len(sp)-1]
}

// Application returns the application of the image,
// e.g. "build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest"
//
//	"mysql-mgr-operator-bundle"
func (n ImageReference) Application() string {
	sp := strings.Split(n.Repo(), "/")
	return sp[len(sp)-1]
}

// Tag returns the tag of the image,
// e.g. "build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest"
//
//	"latest"
func (n ImageReference) Tag() string {
	sp := strings.Split(string(n), ":")
	if len(sp) == 1 {
		return "latest"
	}
	return sp[1]
}

// IsBundle returns true if the image is a bundle image.
// e.g. "build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest"
//
//	true
func (n ImageReference) IsBundle() bool {
	return strings.HasSuffix(n.Repo(), "bundle")
}

func NewImageUnit(reference string, arch string, isPartial bool) *ImageUnit {
	return &ImageUnit{
		Reference: ImageReference(reference),
		Arch:      arch,
		IsPartial: isPartial,
	}
}

type ImageUnit struct {
	Reference ImageReference
	Arch      string
	IsPartial bool
	Features  map[ImageFeature]bool
}

// ReferenceClean returns the arch reference of the image if the image is partial,
// e.g. "build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest", "amd64"
//
//	"build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest-amd64"
func (i *ImageUnit) ReferenceAutoWithArch() string {
	if i.IsPartial {
		return i.Reference.String() + "-" + i.Arch
	}
	return i.Reference.String()
}

// ReferenceClean returns the clean reference of the image,
// e.g. "build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest", "amd64"
//
//	"build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest"
func (i *ImageUnit) ReferenceClean() string {
	return i.Reference.String()
}

func NewMultiArchImage(reference string, architectures []string, features []ImageFeature) *MultiArchImage {
	ImageFeatures := make(map[ImageFeature]bool)
	for _, feature := range features {
		ImageFeatures[feature] = true
	}
	return &MultiArchImage{
		Reference:     ImageReference(reference),
		Architectures: architectures,
		Features:      ImageFeatures,
	}
}

type MultiArchImage struct {
	Reference     ImageReference
	Architectures []string
	Features      map[ImageFeature]bool
}

func (i *MultiArchImage) Action(rules []*Rule) {
	for _, rule := range rules {
		action := rule.AsImageAction()
		if action != nil {
			action.Action(i)
		}
	}
}

func (i *MultiArchImage) IsSingleArch() bool {
	return len(i.Architectures) == 1
}

// IsBundle returns true if the image is a bundle image.
// e.g. "build-harbor.alauda.cn/middleware/mysql-mgr-operator-bundle:latest"
//
//	true
func (i *MultiArchImage) IsBundle() bool {
	return i.Features != nil && i.Features[ImageFeatureBundle]
}

func (i MultiArchImage) GetFirstImage() *ImageUnit {
	isPartial := len(i.Architectures) > 1
	return &ImageUnit{
		Reference: i.Reference,
		Arch:      i.Architectures[0],
		IsPartial: isPartial,
	}
}

func (i *MultiArchImage) GetImageUnits() []*ImageUnit {
	var images []*ImageUnit
	isPartial := len(i.Architectures) > 1
	for _, arch := range i.Architectures {
		images = append(images, &ImageUnit{
			Reference: i.Reference,
			Arch:      arch,
			IsPartial: isPartial,
			Features:  i.Features,
		})
	}
	return images
}
